package wang.aweb.childcar.tcpserver;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ServerHandler extends ChannelInboundHandlerAdapter {
    private static final Logger logger= LoggerFactory.getLogger(NettyServer.class);
    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     * @param ctx
     */
    public static ConcurrentHashMap<ChannelId,ChannelHandlerContext> CHANNEL_MAP=new ConcurrentHashMap<>();
    /**
     * 用户ID=>channel
     */
    public static ConcurrentHashMap<String, String> channelMap = new ConcurrentHashMap<>();
//    public boolean hasUser(Channel channel) {
//        AttributeKey<String> key = AttributeKey.valueOf("user");
//        return (channel.hasAttr(key) || channel.attr(key).get() != null);//netty移除了这个map的remove方法,这里的判断谨慎一点
//    }
    /**
     * 上线一个设备，绑定设备身份
     *
     * @param channelId
     * @param imei
     */
    public void bind(String channelId, String imei) {
        if(channelMap.containsKey(imei)){
            //logger.info("客户端【"+channelId+"】是连接状态，连接通道数量："+CHANNEL_MAP.size());
        }else{
            //保存连接
            channelMap.put(imei,channelId);
        }
    }

    /**
     * 有用户进入
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx){
        System.out.println("channelActive------->");
        InetSocketAddress insocket=(InetSocketAddress)ctx.channel().remoteAddress();
        String clientIp=insocket.getAddress().getHostAddress();
        int clientPort=insocket.getPort();
        //获取连接信道唯一标识
        ChannelId channelId=ctx.channel().id();
        System.out.println();
        if(CHANNEL_MAP.containsKey(channelId)){
            logger.info("客户端【"+channelId+"】是连接状态，连接通道数量："+CHANNEL_MAP.size());
        }else{
            //保存连接
            CHANNEL_MAP.put(channelId,ctx);
            logger.info("客户端【"+channelId+"】连接netty服务器[ip:"+clientIp+"---->port:"+clientPort+"]");
            logger.info("连接通道数量"+CHANNEL_MAP.size());

        }
    }
    private String checkSign(String code) {
        int y=0;
        for(int i=0;i<code.length();i+=2){
            y=y^Integer.parseInt(code.substring(i,i+2),16);
        }

        String res=decHex(y);
        if(res.length()==1){
            res="0"+res;
        }
        return res;
    }

    /**
     * 十六进制转十进制
     * @param num
     * @return
     */
    private String decHex(int num){
        char[] map = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        if(num == 0) return "0";
        String result = "";
        while(num != 0){
            int x = num&0xF;
            result = map[(x)] + result;
            num = (num >>> 4);
        }
        return result;
    }
    private String slImeiToImei(String slImei){
        String imei="";
        for(int i=0;i<slImei.length();i+=2){
            imei=imei+slImei.substring(i+1,i+2);
        }
        return imei;
    }
    private Map codeToMap(String code){
        Map jsonMap=new HashMap();
        jsonMap.put("actionCode",code.substring(4,6));
        String slImei=code.substring(10,40);

        jsonMap.put("imei",this.slImeiToImei(slImei));
        jsonMap.put("timeStamp",code.substring(40,48));
        Integer msgLength=Integer.parseInt(code.substring(8,10),16)*2+10;
        jsonMap.put("otherData",code.substring(48,msgLength));
        jsonMap.put("sign",code.substring(msgLength,msgLength+2));
        jsonMap.put("leng",code.substring(8,10));

        jsonMap.put("length",Integer.parseInt(code.substring(8,10),16));
        return jsonMap;
    }

    /**
     * 有用户退出
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        InetSocketAddress insocket=(InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp=insocket.getAddress().getHostAddress();
        ChannelId channelId=ctx.channel().id();
        //包含此客户端才去删除
        if(CHANNEL_MAP.containsKey(channelId)){
            CHANNEL_MAP.remove(channelId);
            System.out.println();
            logger.info("客户端【"+channelId+"】退出netty服务器[ip:"+clientIp+"---->port:"+insocket.getPort()+"]");
            logger.info("连接通道数量"+CHANNEL_MAP.size());
            String imei=(String) getKey(channelMap,channelId.toString());
            channelMap.remove(imei);
            //
        }
    }
    public static Object getKey(Map map, Object value){
        List<Object> keyList = new ArrayList<>();
        for(Object key: map.keySet()){
            if(map.get(key).equals(value)){
                keyList.add(key);
            }
        }
        return keyList;
    }
    /**
     * 有客户端发送消息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
        System.out.println("Server channelRead......");
        logger.info("加载客户端报文......");
        System.out.println(ctx.channel().remoteAddress()+"---->Server:"+msg.toString());
        this.channelWrite(ctx.channel().id(),msg);
    }

    /**
     * 服务端给客户端发送消息
     * @param channelId  联通信道唯一ID
     * @param msg   需要发送的内容
     * @return :void
     */
    public void channelWrite(ChannelId channelId,Object msg) throws Exception{
        ChannelHandlerContext ctx=CHANNEL_MAP.get(channelId);
        if(ctx==null){
            logger.info("通道【"+channelId+"】不存在");
            return;
        }
        if(msg==null || msg==""){
            logger.info("服务端响应空的消息");
            return;
        }
        String y=checkSign(msg.toString().substring(0,msg.toString().length()-2));
        System.out.println(y);
        if(!y.equals(msg.toString().substring(msg.toString().length()-2,msg.toString().length()))){
            logger.info("发送信息验签失败");
            return;
        }
        Map jsonMap=codeToMap(msg.toString());
        ActionHandler actionHandler=new ActionHandler();
        String resMsg=null;
        switch (jsonMap.get("actionCode").toString()){
            case "10":

                this.bind(ctx.channel().id().toString(),jsonMap.get("imei").toString());
                resMsg=actionHandler.doPingPong(jsonMap);
                logger.info(channelMap.toString());
                logger.info("完成心跳操作");
                break;
            case "11":
                resMsg=actionHandler.doStartEnd(jsonMap);
                logger.info("完成开始结束操作");
                break;
            case "12":
                resMsg=actionHandler.doPlay(jsonMap);
                logger.info("完成游玩操作");
                break;
            default:
                break;
        }
        //reqToResp.doPingPong("123123")
        ctx.write(resMsg);
        ctx.flush();
    }



    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throws Exception{
        System.out.println();
        ctx.close();
        logger.info(ctx.channel().id()+"发生了错误，此链接已关闭"+"此时连通数量："+CHANNEL_MAP.size());
    }

}

